﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using DBCodeBuilder.CodeGeneration.Domain;
using DBCodeBuilder.Core.Classes;
using DBCodeBuilder.Core.Enum;
using DBCodeBuilder.Core.Interfaces;
using DBCodeBuilder.Core.Models;
using HSystem.Data;
using HSystem.Data.DbMetadataClasses;

namespace DBCodeBuilder.CodeGeneration.NHibernateCodeGeneration
{
    public class NHibernateMappingGen : IMappingGenerator
    {
        private StringBuilder builder, builderMapping, listInit, builderLog;
        private MapGenSettings Settings;
        private int Sequenciator;
        private TableModel Model;
        private Dictionary<string, string> ColumnClassDictionary;

        public string Code
        {
            get { return builder.ToString(); }
        }

        public string CodeMapping
        {
            get { return builderMapping.ToString(); }
        }

        public string GeneratedClassName
        {
            get { return BuildClassName(); }
        }

        public string LogMessage
        {
            get { return builderLog.ToString(); }
        }

        public void Generate(string owner, string tableName, MapGenSettings settings)
        {
            Model = new TableModel { Owner = owner, Name = tableName, ObjectType = DbObjectType.Table };
            Settings = settings;

            Model.MetaData = DbMetadata.GetTable(owner + "." + tableName, "app", true, true, true, false, true);
            if (Model.MetaData == null)
            {
                Model.MetaData = DbMetadata.GetView(owner + "." + tableName, "app");
                if (Model.MetaData == null)
                    throw new Exception("Non è stata trovata nessuna tabella o vista col nome/schema fornito: " + owner +
                                        "." + tableName);

                Model.ObjectType = DbObjectType.View;
            }
            Model.UserMapping = null;

            GenerateCode();
        }

        public void Generate(TableModel md, MapGenSettings settings)
        {
            Model = md;
            if (Model.ForcedPrimaryKey == null)
                Model.ForcedPrimaryKey = "";
            Settings = settings;

            GenerateCode();
        }

        private void GenerateCode()
        {
            ColumnClassDictionary = new Dictionary<string, string>();
            builder = new StringBuilder();
            builderMapping = new StringBuilder();
            listInit = new StringBuilder();
            builderLog = new StringBuilder();
            Sequenciator = 0;

            EmitClassName();
            EmitProps();
            EmitManyToOne();
            EmitBags();
            EmitEndClass();

            //Replace placeholder with list initializations
            builder.Replace("{LIST_INIT}", listInit.ToString());

            //If we don't have a list to initialize in our code, we don't need a constructor
            if (!String.IsNullOrEmpty(listInit.ToString()))
            {
                builder.Replace("~", String.Empty);
                return;
            }
            var idx1 = builder.ToString().IndexOf("~");
            var idx2 = builder.ToString().LastIndexOf("~", StringComparison.Ordinal);
            builder.Remove(idx1, idx2 - idx1 + 1);
        }

        /// <summary>
        /// Usage: String.Format(GetFileStructure(...), *CodiceGenerato*)
        /// </summary>
        /// <param name="tab"></param>
        /// <param name="nameSp"></param>
        /// <param name="toGen"></param>
        /// <param name="referencedNamespace"></param>
        /// <returns></returns>
        public string GetFileStructure(TableModel tab, string nameSp, CodeFileStructure toGen,
            string referencedNamespace)
        {
            StringBuilder builderStructure = new StringBuilder();

            if (toGen == CodeFileStructure.Domain)
            {
                //Horrible but works
                if (tab.GeneratedClass.Contains("DateTime"))
                    builderStructure.AppendFormat("using System;\r\n");
            }
            if (toGen == CodeFileStructure.Domain)
            {
                //Horrible but works
                if (tab == null)
                    builderStructure.AppendFormat("using System.Collections.Generic;\r\n\r\n");
                else if (tab.GeneratedClass.Contains("IList"))
                    builderStructure.AppendFormat("using System.Collections.Generic;\r\n\r\n");
                else
                    builderStructure.AppendFormat("\r\n");
            }
            else
            {
                if (!String.IsNullOrEmpty(referencedNamespace))
                    builderStructure.AppendFormat("using {0};\r\n", referencedNamespace);

                //Horrible but works
                if (tab == null)
                    builderStructure.AppendFormat("using NHibernate.Mapping.ByCode;\r\n");
                else if (tab.GeneratedMapping.Contains("Generator") || tab.GeneratedMapping.Contains("Cascade"))
                    builderStructure.AppendFormat("using NHibernate.Mapping.ByCode;\r\n");

                builderStructure.AppendFormat("using NHibernate.Mapping.ByCode.Conformist;\r\n\r\n");
            }
            builderStructure.AppendFormat("namespace {0}\r\n", nameSp);
            builderStructure.AppendFormat("{{{{\r\n");
            builderStructure.Append("{0}");
            builderStructure.AppendFormat("}}}}");
            return builderStructure.ToString();
        }

        private void EmitClassName()
        {
            builder.AppendFormat("\tpublic{1}class {0}\r\n", GeneratedClassName,
                Settings.GeneretePartialDomain ? " partial " : " ");
            builder.AppendFormat("\t{{\r\n");

            builder.AppendFormat("~");
            builder.AppendFormat("\t\tpublic {0}()\r\n", GeneratedClassName);
            builder.AppendFormat("\t\t{{\r\n");
            builder.AppendFormat("{{LIST_INIT}}");
            builder.AppendFormat("\t\t}}\r\n\r\n");
            builder.AppendFormat("~");

            builderMapping.AppendFormat("\tpublic class {0}Map : ClassMapping<{0}>\r\n", GeneratedClassName);
            builderMapping.AppendFormat("\t{{\r\n");
            builderMapping.AppendFormat("\t\tpublic {0}Map()\r\n", GeneratedClassName);
            builderMapping.AppendFormat("\t\t{{\r\n");
            builderMapping.AppendFormat("\t\t\tTable(\"{0}\");\r\n", Model.Name);
            builderMapping.AppendFormat("\t\t\tSchema(\"{0}\");\r\n", Model.Owner);
            builderMapping.AppendFormat("\t\t\tLazy(true);\r\n");

            var MetaData = Model.MetaData;
            if (!MetaData.HasPrimaryKey)
            {
                if (Model.ObjectType == DbObjectType.View)
                {
                    var primaryKeyFields = Model.ForcedPrimaryKey.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                    if (!primaryKeyFields.Any())
                        builderMapping.AppendFormat("\t\t\tId(x => x.{0}, map => map.Generator(Generators.Assigned));\r\n", "IDFAKE");
                    else if (primaryKeyFields.Count() == 1)
                        builderMapping.AppendFormat("\t\t\tId(x => x.{0}, map => map.Generator(Generators.Assigned));\r\n", primaryKeyFields[0]);
                    else
                        ComposeMultiID(primaryKeyFields.ToList());
                }
                return;
            }

            if (MetaData.PrimaryKey.ColumnNames.Count == 1)
            {
                var pkName = MetaData.PrimaryKey.ColumnNames[0];
                if (MetaData.HasSequence)
                {
                    builderMapping.AppendFormat("\t\t\tId(x => x.{0}, map =>\r\n", pkName);
                    builderMapping.AppendFormat("\t\t\t{{\r\n");
                    builderMapping.AppendFormat("\t\t\t\tmap.Column(\"{0}\");\r\n", pkName);

                    if (!MetaData.HasSequence)
                        builderMapping.AppendFormat("\t\t\t\tmap.Generator(Generators.Assigned);\r\n");
                    else
                        builderMapping.AppendFormat("\t\t\t\tmap.Generator(Generators.Sequence, g => g.Params(new {{ sequence = \"{0}.{1}\" }}));\r\n", Model.Owner, MetaData.SequenceName);

                    builderMapping.AppendFormat("\t\t\t}});\r\n");
                }
                else
                    builderMapping.AppendFormat("\t\t\tId(x => x.{0}, map => map.Column(\"{0}\"));\r\n", pkName);
            }
            else
                ComposeMultiID(MetaData.PrimaryKey.ColumnNames);
        }

        private void ComposeMultiID(IEnumerable<string> list)
        {
            var MetaData = Model.MetaData;
            var tocycle = MetaData.ForeignKeys.Where(t => t.IsParent).ToList();
            var fksUsed = GetUsedForeignKeys(tocycle);

            //If the relationship exists but is setted disabled via designer i act like it doesn't exist
            List<MetaDataForeignKey> fkFiltered;
            if (Model.References.Count > 0 && String.IsNullOrEmpty(Model.References[0].NameOfRepresentingFK))
                fkFiltered = fksUsed;
            else
                fkFiltered = fksUsed.Where(key => Model.References.Single(t => t.NameOfRepresentingFK == key.Name).Generate).ToList();

            builderMapping.AppendFormat("\t\t\tComposedId(mapper =>\r\n");
            builderMapping.AppendFormat("\t\t\t{{\r\n");

            foreach (string pkCol in list)
            {
                var fk = fkFiltered.SingleOrDefault(t => t.BaseColumnNames.Contains(pkCol) && t.BaseColumnNames.Count == 1);
                if (fk != null)
                {
                    if (fk.BaseColumnNames.Count == 1)
                    {
                        string refClassName;
                        var referencedModel = ModelContainer.DrawingTableModels.Single(t => t.Owner == fk.Reference.Owner && t.Name == fk.Reference.TableName);
                        if (!String.IsNullOrEmpty(referencedModel.UserMapping.ClassName))
                            refClassName = referencedModel.UserMapping.ClassName;
                        else
                            refClassName = BuildClassName(fk.Reference.Owner, fk.Reference.TableName);

                        ColumnClassDictionary.Add(pkCol, refClassName);
                        builderMapping.AppendFormat("\t\t\t\tmapper.ManyToOne(p => p.{0}, p => p.Column(\"{1}\"));\r\n", refClassName, pkCol);
                    }
                    //if a 2+ columns primary key is also a 2+ columns foreign key, do nothing (for now)
                }
                else
                    builderMapping.AppendFormat("\t\t\t\tmapper.Property(x => x.{0});\r\n", pkCol);
            }

            builderMapping.AppendFormat("\t\t\t}});\r\n");
        }

        private void EmitProps()
        {
            var MetaData = Model.MetaData;
            var tocycle = MetaData.ForeignKeys.Where(t => t.IsParent).ToList();
            var fksUsed = GetUsedForeignKeys(tocycle);
            var eventuallyForcedFks = Model.ForcedPrimaryKey.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            List<string> primaryKeyColumns = new List<string>();
            List<MetaDataForeignKey> fkFiltered;

            //Exclude existing foreign keys when they are set to be not generated
            if (Model.References.Count > 0 && String.IsNullOrEmpty(Model.References[0].NameOfRepresentingFK))
                fkFiltered = fksUsed;
            else
                fkFiltered = fksUsed.Where(key => Model.References.Single(t => t.NameOfRepresentingFK == key.Name).Generate).ToList();

            //Generates properties for everything excluding primary and foreign keys(if generated)
            List<string> constrainedcolumns = fkFiltered.Where(t => t.Reference.Owner == Model.Owner).SelectMany(key => key.BaseColumnNames).ToList();

            if (MetaData.HasPrimaryKey)
                primaryKeyColumns.AddRange(MetaData.PrimaryKey.ColumnNames);
            else if (Model.ObjectType == DbObjectType.View && eventuallyForcedFks.Any())
                primaryKeyColumns.AddRange(eventuallyForcedFks);
            else
            {
                if (Model.ObjectType == DbObjectType.Table)
                    builderLog.AppendFormat("WARNING: No keys found for selected table entity: {0}.{1}\r\n", Model.Owner, Model.Name);
                else
                {
                    if (String.IsNullOrEmpty(Model.ForcedPrimaryKey))
                        builder.AppendFormat("\t\tpublic virtual int IDFAKE {{ get; set; }}\r\n");
                }
            }

            foreach (var col in MetaData.Columns.OrderBy(t => t.Position))
            {
                //If the column is already an entity (foreign key) ignores everything
                if (constrainedcolumns.Contains(col.Name))
                    continue;

                //if it's primary key i need the property in the domain
                builder.AppendFormat("\t\tpublic virtual {0} {1} {{ get; set; }}\r\n",
                    Utility.GetCSType(col.DbHType, col.Nullable), col.Name);
                if (!primaryKeyColumns.Contains(col.Name))
                    builderMapping.AppendFormat("\t\t\tProperty(x => x.{0});\r\n", col.Name);
            }
        }

        private void EmitManyToOne()
        {
            var MetaData = Model.MetaData;
            var tocycle = MetaData.ForeignKeys.Where(t => t.IsParent).ToList();
            var fksUsed = GetUsedForeignKeys(tocycle);

            //Exclude existing foreign keys when they are set to be not generated
            List<MetaDataForeignKey> fkFiltered;
            if (Model.References.Count > 0 && String.IsNullOrEmpty(Model.References[0].NameOfRepresentingFK))
                fkFiltered = fksUsed;
            else
                fkFiltered = fksUsed.Where(key => Model.References.Single(t => t.NameOfRepresentingFK == key.Name).Generate).ToList();

            foreach (MetaDataForeignKey fk in fkFiltered)
            {
                bool multiplefksOnSameTable = IsMultipleFksToSameTable(fksUsed, fk);
                var referencedModel = ModelContainer.DrawingTableModels.Single(t => t.Owner == fk.Reference.Owner && t.Name == fk.Reference.TableName);
                var thisModel = ModelContainer.DrawingTableModels.Single(t => t.Owner == MetaData.Schema && t.Name == MetaData.Name);
                var relationship = thisModel.References.First(t => t.RefOwner == referencedModel.Owner && t.RefTable == referencedModel.Name);

                string refClassName;

                if (!String.IsNullOrEmpty(referencedModel.UserMapping.ClassName))
                    refClassName = referencedModel.UserMapping.ClassName;
                else
                    refClassName = BuildClassName(fk.Reference.Owner, fk.Reference.TableName);

                var modifier = GetFKModifier(multiplefksOnSameTable, fk);
                builder.AppendFormat("\t\tpublic virtual {0} {0}{1} {{ get; set; }}\r\n", refClassName, modifier);
                builderMapping.AppendFormat("\t\t\tManyToOne(x => x.{0}, map =>\r\n", refClassName + modifier);
                builderMapping.AppendFormat("\t\t\t\t{{\r\n");

                if (fk.BaseColumnNames.Count == 1)
                    builderMapping.AppendFormat("\t\t\t\t\tmap.Column(\"{0}\");\r\n", fk.BaseColumnNames[0]);
                else
                {
                    string columns = fk.BaseColumnNames.Aggregate("",
                        (current, bcN) => current + String.Format("k => k.Name(\"{0}\"), ", bcN));
                    columns = columns.TrimEnd(' ', ',');

                    builderMapping.AppendFormat("\t\t\t\t\tmap.Columns({0});\r\n", columns);
                }
                builderMapping.AppendFormat("\t\t\t\t\tmap.NotNullable({0});\r\n",
                    relationship.NullableDirect ? "false" : "true");
                builderMapping.AppendFormat("\t\t\t\t\tmap.Cascade(Cascade.{0});\r\n", relationship.CascadeStyleDirect);
                builderMapping.AppendFormat("\t\t\t\t\tmap.Lazy({0});\r\n", relationship.LazyLoadingDirect ? "LazyRelation.Proxy" : "LazyRelation.NoLazy");
                builderMapping.AppendFormat("\t\t\t\t}});\r\n");
            }
        }

        private void EmitBags()
        {
            var MetaData = Model.MetaData;
            var tocycle = MetaData.ForeignKeys.Where(t => !t.IsParent).ToList();
            var fksUsed = GetUsedForeignKeys(tocycle);

            foreach (MetaDataForeignKey fk in fksUsed)
            {
                var referenceModel = ModelContainer.DrawingTableModels.SingleOrDefault(t => t.Owner == fk.Reference.Owner && t.Name == fk.Reference.TableName);

                //Blocks out bad configurations
                if (!String.IsNullOrEmpty(referenceModel.References[0].NameOfRepresentingFK))
                {
                    var reference = referenceModel.References.Single(t => t.NameOfRepresentingFK == fk.Name);
                    if (!reference.GenerateInverse || !reference.Generate)
                        continue;
                }

                var parentFkEquivalent =
                    ModelContainer.DrawingTableModels.SelectMany(t => t.MetaData.ForeignKeys)
                        .Single(f => f.Name == fk.Name && f.IsParent);
                bool multiplefksOnSameTable = IsMultipleFksToSameTable(fksUsed, fk);
                var thisModel =
                    ModelContainer.DrawingTableModels.SingleOrDefault(
                        t => t.Owner == MetaData.Schema && t.Name == MetaData.Name);
                var relationship =
                    referenceModel.References.Single(t => t.RefOwner == thisModel.Owner && t.RefTable == thisModel.Name);

                string refSetName = String.IsNullOrEmpty(referenceModel.UserMapping.SetName)
                    ? BuildClassName(fk.Reference.Owner, fk.Reference.TableName)
                    : referenceModel.UserMapping.SetName;
                string refClassName = String.IsNullOrEmpty(referenceModel.UserMapping.ClassName)
                    ? BuildClassName(fk.Reference.Owner, fk.Reference.TableName)
                    : referenceModel.UserMapping.ClassName;

                if (relationship.RelType == DbRelationshipType.OneToOne)
                {
                    builder.AppendFormat("\t\tpublic virtual {0} {1} {{ get; set; }}\r\n", refClassName, refClassName);
                    builderMapping.AppendFormat("\t\t\tOneToOne(x => x.{0}, m => \r\n", refClassName);
                    builderMapping.AppendFormat("\t\t\t{{\r\n");
                    builderMapping.AppendFormat("\t\t\t\tm.Cascade(Cascade.{0});\r\n", relationship.CascadeStyleDirect);
                    builderMapping.AppendFormat("\t\t\t\tm.Constrained(true);\r\n");
                    builderMapping.AppendFormat("\t\t\t\tm.Lazy({0});\r\n", relationship.LazyLoadingInverse ? "LazyRelation.Proxy" : "LazyRelation.NoLazy");
                    builderMapping.AppendFormat("\t\t\t\tm.PropertyReference(typeof({0}).GetPropertyOrFieldMatchingName(\"{1}\"));\r\n", refClassName, BuildClassName(thisModel));
                    builderMapping.AppendFormat("\t\t\t\tm.OptimisticLock(true);\r\n");
                    builderMapping.AppendFormat("\t\t\t\tm.Access(Accessor.Property);\r\n");
                    builderMapping.AppendFormat("\t\t\t}});\r\n");
                }
                else
                {
                    var modifier = GetFKModifier(multiplefksOnSameTable, fk);
                    bool ListModifierNeeded = BuildClassName(fk.Reference.Owner, fk.Reference.TableName) == refSetName;
                    builder.AppendFormat("\t\tpublic virtual IList<{0}> {2}{1}{3} {{ get; set; }}\r\n", refClassName, refSetName, ListModifierNeeded ? "List" : "", modifier);
                    listInit.AppendFormat("\t\t\t{2}{1}{3} = new List<{0}>();\r\n", refClassName, refSetName, ListModifierNeeded ? "List" : "", modifier);

                    string columns;
                    if (parentFkEquivalent.BaseColumnNames.Count > 1)
                        columns = fk.Reference.ColumnNames.Aggregate("", (current, bcN) => current + String.Format("k => k.Name(\"{0}\"), ", bcN)).TrimEnd(' ', ',');
                    else
                        columns = String.Format("\"{0}\"", parentFkEquivalent.BaseColumnNames[0]);

                    builderMapping.AppendFormat("\t\t\tBag(x => x.{1}{0}, colmap =>\r\n", refSetName + modifier, ListModifierNeeded ? "List" : "");
                    builderMapping.AppendFormat("\t\t\t\t{{\r\n");
                    builderMapping.AppendFormat("\t\t\t\t\tcolmap.Key(map => map.Column{1}({0}));\r\n", columns, parentFkEquivalent.BaseColumnNames.Count > 1 ? "s" : "");
                    builderMapping.AppendFormat("\t\t\t\t\tcolmap.Inverse(true);\r\n");
                    builderMapping.AppendFormat("\t\t\t\t\tcolmap.Cascade(Cascade.{0});\r\n", relationship.CascadeStyleInverse);
                    builderMapping.AppendFormat("\t\t\t\t\tcolmap.Lazy({0});\r\n", relationship.LazyLoadingInverse ? "CollectionLazy.Lazy" : "CollectionLazy.NoLazy");
                    builderMapping.AppendFormat("\t\t\t\t}}, map => map.OneToMany());\r\n");
                }
            }
        }

        private bool IsMultipleFksToSameTable(List<MetaDataForeignKey> fksUsed, MetaDataForeignKey fkBase)
        {
            List<string> referencedTables = new List<string>();
            foreach (var key in fksUsed)
            {
                if (!referencedTables.Contains(key.Reference.TableName))
                    referencedTables.Add(key.Reference.TableName);
                else
                    return key.Reference.TableName == fkBase.Reference.TableName;
            }
            return false;
        }

        private string GetFKModifier(bool multiplefksOnSameTable, MetaDataForeignKey fk)
        {
            string cols = "";
            if (!multiplefksOnSameTable)
                return cols;

            cols = fk.BaseColumnNames.Aggregate("", (current, bcN) => current + bcN);
            cols = cols.TrimEnd(' ', ',');
            if (String.IsNullOrEmpty(cols))
                cols = Sequenciator++.ToString();
            return cols;
        }

        private void EmitEndClass()
        {
            var eventuallyForcedFks = Model.ForcedPrimaryKey.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            var MetaData = Model.MetaData;
            //With composite primary keys, NHibernate requires the override of Equals and GetHashCode
            if ((MetaData.HasPrimaryKey && MetaData.PrimaryKey.ColumnNames.Count > 1) || Settings.GenereteDomainEqualityComparer || eventuallyForcedFks.Count() > 1)
            {
                builder.AppendFormat("\r\n\t\tpublic override bool Equals(object obj)\r\n");
                builder.AppendFormat("\t\t{{\r\n");
                builder.AppendFormat("\t\t\tif (obj == null) return false;\r\n");
                builder.AppendFormat("\t\t\tvar t = obj as {0};\r\n", GeneratedClassName);
                builder.AppendFormat("\t\t\tif (t == null) return false;\r\n");
                string aggregate;
                if (MetaData.HasPrimaryKey)
                    aggregate = MetaData.PrimaryKey.ColumnNames.Aggregate("", (current, name) => current + String.Format("{0}{1} == t.{0}{1} &&\r\n\t\t\t\t", (ColumnClassDictionary.ContainsKey(name) ? ColumnClassDictionary[name] + "." : ""), name));
                else if (eventuallyForcedFks.Count() > 1)
                    aggregate = eventuallyForcedFks.Aggregate("", (current, name) => current + (name + " == t." + name + " &&\r\n\t\t\t\t"));
                else
                    aggregate = MetaData.Columns.Select(t => t.Name).Aggregate("", (current, name) => current + (name + " == t." + name + " &&\r\n\t\t\t\t"));
                aggregate = aggregate.TrimEnd('\t').TrimEnd(Environment.NewLine.ToCharArray()).TrimEnd('&');

                builder.AppendFormat("\t\t\tif ({0}) \r\n", aggregate);
                builder.AppendFormat("\t\t\t\treturn true;\r\n\r\n");
                builder.AppendFormat("\t\t\treturn false;\r\n");
                builder.AppendFormat("\t\t}}\r\n");

                builder.AppendFormat("\r\n\t\tpublic override int GetHashCode()\r\n");
                builder.AppendFormat("\t\t{{\r\n");
                builder.AppendFormat("\t\t\tint hash = GetType().GetHashCode();\r\n");
                if (MetaData.HasPrimaryKey)
                    aggregate = MetaData.PrimaryKey.ColumnNames.Aggregate("", (current, name) => current + ("hash = (hash * 397) ^ " + (ColumnClassDictionary.ContainsKey(name) ? ColumnClassDictionary[name] + "." : "") + name + ".GetHashCode();\r\n\t\t\t"));
                else if (eventuallyForcedFks.Count() > 1)
                    aggregate = eventuallyForcedFks.Aggregate("", (current, name) => current + ("hash = (hash * 397) ^ " + name + ".GetHashCode();\r\n\t\t\t"));
                else
                    aggregate = MetaData.Columns.Select(t => t.Name).Aggregate("", (current, name) => current + ("hash = (hash * 397) ^ " + name + ".GetHashCode();\r\n\t\t\t"));

                aggregate = aggregate.TrimEnd('\t');
                builder.AppendFormat("\t\t\t{0}", aggregate);

                builder.AppendFormat("\t\t\treturn hash;\r\n");
                builder.AppendFormat("\t\t}}\r\n");
            }

            builder.AppendFormat("\t}}\r\n");
            builderMapping.AppendFormat("\t\t}}\r\n\t}}\r\n");
        }

        private string BuildClassName(string owner = "", string tableName = "")
        {
            string ret;
            if (String.IsNullOrEmpty(owner) && String.IsNullOrEmpty(tableName))
            {
                if (!String.IsNullOrEmpty(Model.UserMapping.ClassName))
                    return Model.UserMapping.ClassName;

                ret = (Model.Owner + " " + Model.Name).Replace("_", " ");
            }
            else
            {
                ret = (owner + " " + tableName).Replace("_", " ");
            }
            ret = ret.Trim().ToLower();
            ret = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(ret);
            ret = ret.Replace(" ", "");
            return ret;
        }

        private List<MetaDataForeignKey> GetUsedForeignKeys(IEnumerable<MetaDataForeignKey> tocycle)
        {
            var ret = new List<MetaDataForeignKey>();
            foreach (MetaDataForeignKey fk in tocycle)
            {
                var referencedModel = ModelContainer.DrawingTableModels.SingleOrDefault(t => t.Owner == fk.Reference.Owner && t.Name == fk.Reference.TableName);

                if (referencedModel == null)
                    continue;

                ret.Add(fk);
            }
            return ret;
        }

        private string BuildClassName(TableModel model)
        {
            return !String.IsNullOrEmpty(model.UserMapping.ClassName) ? model.UserMapping.ClassName : BuildClassName(model.Owner, model.Name);
        }
    }
}